﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.Mvc;
using Sendz.WebUI.Authorization;
using Sendz.WebUI.Helpers;
using Sendz.WebUI.Models;
using Sendz.Domain;
using Sendz.Model;
using Microsoft.Security.Application;
using System.Configuration;
using System.IO;
using System.Text.RegularExpressions;

namespace Sendz.WebUI.Controllers
{
    // Ref: http://stackoverflow.com/questions/759519/c-centralizing-repeating-viewdata-in-mvc
    [UserInfo] // Reference UserHelpers.cs for attribute override method; allows global user parameter access
    public class AutoResponderController : AjaxFriendlyController
    {
        #region Repository injection //////////////////////////////////

        // Aggregate repositories, injected by container factory in /Infrastructure
        private readonly IUserRepository dbUser;
        private readonly IIdentityRepository dbIdentity;
        private readonly IFormRepository dbForm;
        private readonly IAutoResponderRepository dbAutoResponder;
        public AutoResponderController(IUserRepository dbUser, IIdentityRepository dbIdentity, IFormRepository dbForm, IAutoResponderRepository dbAutoResponder)
        {
            this.dbUser = dbUser;
            this.dbIdentity = dbIdentity;
            this.dbForm = dbForm;
            this.dbAutoResponder = dbAutoResponder;
        }

        #endregion

        #region Action: List Index ////////////////////////////

        [Authorize]
        public ViewResult List()
        {
            // Retrieve current, logged-in user
            var user = Membership.GetUser(true) as CustomMembershipUser;
            return View(dbAutoResponder.GetAutoRespondersByUserKey((Guid)user.ProviderUserKey).OrderByDescending(f => f.LastModified));
        }

        #endregion

        #region Action: Create (Uses Edit) ////////////////////////////

        [Authorize]
        [HttpGet]
        public ViewResult Create()
        {
            // Retrieve current, logged-in user
            var user = Membership.GetUser(true) as CustomMembershipUser;
            // Set a reference point to this being a new autoresponder
            ViewData["IsNewAutoResponder"] = true;
            // Process new forms through the HttpPost handler for the "Edit" action
            return View("Edit", new AutoResponder((Guid)user.ProviderUserKey));
        }

        #endregion

        #region Action: Edit ////////////////////////////

        [Authorize]
        [HttpGet]
        public ViewResult Edit(Guid id)
        {
            var autoResponder = dbAutoResponder.GetAutoResponderByKey(id);
            // Todo: make this DRY since it appears here in HttpGet and Post
            var matchingForms = dbForm.GetFormsUsingAutoResponder(id);
            if (matchingForms.Count > 0)
            {
                var formFieldList = new List<SelectListItem>();
                // Todo: allow the selection of fields from any form using the AutoResponder
                var formFields = dbForm.GetFieldsByFormKey(matchingForms[0].FormKey);
                foreach (var field in formFields)
                {
                    formFieldList.Add(new SelectListItem { Text = field.Name, Value = "[%" + field.Name + "%]" });
                }
                ViewData["FormFieldList"] = formFieldList;
                ViewData["FormFieldCount"] = formFields.Count();
            }
            // Set a reference point to this being a new autoresponder
            ViewData["IsNewAutoResponder"] = false;
            return View(autoResponder);
        }      

        [Authorize]
        [HttpPost]
        [ValidateInput(false)] // Used to allow WYSIWYG editor posts
        public ActionResult Edit(AutoResponder autoResponder, bool isNewAutoResponder)
        {
            var user = Membership.GetUser(true) as CustomMembershipUser;
            // Cleanse the input (since this goes in an email)
            autoResponder.FromName = AntiXss.GetSafeHtmlFragment(autoResponder.FromName);
            autoResponder.Subject = AntiXss.GetSafeHtmlFragment(autoResponder.Subject);
            // Cleanse the returned HTML (since we allow an editor for this); do pre .IsValid since error results need to re-display this
            autoResponder.Body = AntiXss.GetSafeHtmlFragment(autoResponder.Body); // autoResponder.Body = AntiXss.GetSafeHtml(HttpUtility.UrlDecode(autoResponder.Body, System.Text.Encoding.Default));
            if (ModelState.IsValid)
            {
                if (dbAutoResponder.SaveAutoResponder(autoResponder))
                {
                    // Determine if the email from address is validated, set-up validation alert if not
                    // Todo: update the autoresponder to not send if email is not yet validated
                    if (!dbUser.IsEmailValidated((Guid)user.ProviderUserKey, autoResponder.FromEmail))
                    {
                        var thisValidationKey = dbUser.CreateEmailValidation((Guid)user.ProviderUserKey, autoResponder.FromEmail);
                        var emailHelper = new EmailHelpers();
                        emailHelper.SendEmail(ConfigurationManager.AppSettings.Get("FromAddressAdmin"), autoResponder.FromEmail, "Please validate your email address", "Until you click the link below, no emails can be sent from this email address. Thanks for helping Frontdesk ensure that we remain compliant with best-practice email policies. <br /><br /><a href=\"http://getfrontdesk.com/Account/ValidateEmail/" + thisValidationKey + "\">http://getfrontdesk.com/Account/ValidateEmail/" + thisValidationKey + "</a>");
                        TempData["Message"] = "Your autoresponder was saved and a validation link was sent to the email from address you provided";
                    }
                    TempData["Message"] = (TempData["Message"] == null) ? "Your autoresponder was successfully saved" : TempData["Message"];
                    // Todo: manage this workflow better... use a more abstracted intervention strategy
                    // Check if they're a new user and if so, route them to their new form with a note to update the autoresponder
                    var myForms = dbForm.GetFormsByUserKey((Guid)user.ProviderUserKey);
                    if (myForms.Count == 1)
                    {
                        var timeDiff = DateTime.Now.Subtract(myForms[0].CreatedOn);
                        if (myForms[0].AutoResponderKey == null && ((timeDiff.Days * 1440 + timeDiff.Hours * 60 + timeDiff.Minutes) <= 30))
                        {
                            TempData["AutoResponderMessage"] = "Try assigning your new autoresponder to your form! Once you have linked your form to an autoresponder, the next time you edit your autoresponder you can add form fields to it! When people fill out your form, they'll get a customized response... Be sure to check it out.";
                            return Redirect("/Form/Edit/" + myForms[0].FormKey);
                        }
                    }
                    // Confirmation message valid for a single redirect
                    return Redirect("/AutoResponder/List");
                }
            }
            // Otherwise, reload the view (& display errors)
            // Todo: make this DRY since it appears here in HttpGet and Post
            var matchingForms = dbForm.GetFormsUsingAutoResponder(autoResponder.AutoResponderKey);
            if (matchingForms.Count > 0)
            {
                var formFieldList = new List<SelectListItem>();
                // Todo: allow the selection of fields from any form using the AutoResponder
                var formFields = dbForm.GetFieldsByFormKey(matchingForms[0].FormKey);
                foreach (var field in formFields)
                {
                    formFieldList.Add(new SelectListItem { Text = field.Name, Value = "[%" + field.Name + "%]" });
                }
                ViewData["FormFieldList"] = formFieldList;
            }
            // Load with a full reload method so the attachments are reloaded upon error
            var thisAutoResponder = (isNewAutoResponder) ? new AutoResponder((Guid)user.ProviderUserKey) : dbAutoResponder.GetAutoResponderByKey(autoResponder.AutoResponderKey);
            return View(thisAutoResponder);
        }

        #endregion

        #region Actions: Attachments ////////////////////////////

        [Authorize]
        [HttpPost]
        public PartialViewResult UploadFile(Guid autoResponderKey)
        {
            // Ref: http://jquery.malsup.com/form/#file-upload
            // (Ref for regular uploads: http://www.hanselman.com/blog/ABackToBasicsCaseStudyImplementingHTTPFileUploadWithASPNETMVCIncludingTestsAndMocks.aspx)
            var user = Membership.GetUser(true) as CustomMembershipUser;
            var uploadPath = AppDomain.CurrentDomain.BaseDirectory + ConfigurationManager.AppSettings.Get("AppUploadBasePath") + @"\" + user.UserName + @"\";
            var relPath = ConfigurationManager.AppSettings.Get("AppUploadBasePath") + @"\" + user.UserName + @"\";
            // var uploadPath = Server.MapPath(@"..\" + relPath); // Alternate method tested during debugging
            // Create the directory for the user if it doesn't exist
            if (!Directory.Exists(uploadPath))
                Directory.CreateDirectory(uploadPath); // Removed ToLowerInvariant()
            // Deal with the file upload (use loop foreach in case we do multiple uploads in the future)
            // Ref: http://valums.com/ajax-upload/
            foreach (string file in Request.Files)
            {
                var hpf = Request.Files[file] as HttpPostedFileBase;
                // Avoid blank or large files (over 100 kB)
                if (hpf.ContentLength == 0 || (hpf.ContentLength / 1000) > 100)
                    continue;
                var fileName = Path.GetFileName(hpf.FileName);
                // Ensure file type is acceptible
                var fileRegex = new Regex(@"^.*\.(jpg|JPG|gif|GIF|png|PNG|doc|DOC|pdf|PDF|txt|TXT)$");
                if (!fileRegex.Match(fileName).Success)
                    continue;
                // Add _n to the filename if it already exists...
                string savedFileName = Path.Combine(
                    uploadPath,
                    fileName
                );
                var fileIncrement = 2;
                var arrFileName = fileName.Split('.');
                var templateFileName = string.Empty;
                for (var i = 0; i < arrFileName.Count(); i++) 
                {
                    if (i == arrFileName.Count() - 1)
                        templateFileName += "_%%" + "." + arrFileName[i];
                    else
                        templateFileName += (i == 0) ? arrFileName[i] : "." + arrFileName[i];
                }
                while (System.IO.File.Exists(savedFileName))
                {
                    fileName = templateFileName.Replace("%%", fileIncrement.ToString());
                    savedFileName = Path.Combine(uploadPath, fileName);
                    fileIncrement++;
                }
                try
                {
                    hpf.SaveAs(savedFileName);
                    var newAttachment = new Attachment((Guid)user.ProviderUserKey, autoResponderKey);
                    newAttachment.FileName = fileName;
                    newAttachment.FilePath = savedFileName;
                    newAttachment.RelPath = relPath + fileName;
                    // Get size in kB
                    newAttachment.FileSize = Convert.ToInt16(hpf.InputStream.Length / 1000);
                    dbAutoResponder.AddAttachment(newAttachment);
                    return PartialView("file_attachment", newAttachment);
                    // return new AjaxViewResult("file_attachment", newAttachment, ViewData);
                }
                catch (Exception ex)
                {
                    // Todo: Remove this, for now leave in exception throw so Fiddler can see what happened
                    throw ex;
                }
            }
            return null;
        }

        [Authorize]
        [HttpGet]
        public bool DeleteFile(Guid id)
        {
            var thisAttachment = dbAutoResponder.RemoveAttachment(id);
            if (thisAttachment != null)
            {
                try
                {
                    System.IO.File.Delete(thisAttachment.FilePath);
                    return true;
                }
                catch
                {
                    return false;
                }
            }
            return false;
        }

        #endregion

        #region Action: Remove ////////////////////////////

        [Authorize]
        [HttpGet]
        public ActionResult Remove(Guid id)
        {
            if (dbAutoResponder.RemoveAutoResponder(id))
                TempData["Message"] = "Your autoresponder was successfully removed";
            else
                TempData["Message"] = "Your autoresponder couldn't be removed";
            return Redirect("/AutoResponder/List");
        }

        #endregion
    }
}
